﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Windows;
using OpenLS.Spreadsheet.Formats.Biff.OfficeArt;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    internal class TxORecord : BiffRecord
    {
        private ushort _cbRuns;
        private ushort _cchText;
        private ControlInfo _controlInfo;
        private bool _fJustLast;
        private bool _fLockText;
        private bool _fSecretEdit;
        private ObjFmla _fmla;
        private byte _hAlignment = 1;
        private ushort _ifntEmpty;
        private byte[] _reserved;
        private ushort _rot;

        private TxoRuns _runs;
        private XLUnicodeStringNoCch _stringNoCch;
        private byte _vAlignment = 1;

        public TxORecord(RichTextParagraph text, OfficeArtWriteContext context)
        {
            _runs = new TxoRuns(text, context);
            string s = text.Text;
            _stringNoCch = (XLUnicodeStringNoCch) s;
            _cchText = (ushort) s.Length;
            _fmla = new ObjFmla();
            _fLockText = true;
        }

        public TxORecord()
        {
        }

        private ushort Ah
        {
            get
            {
                ushort result = 0;
                result |= (ushort) (_hAlignment << 1);
                result |= (ushort) (_vAlignment << 4);
                if (_fLockText)
                    result |= 0x200;
                if (_fJustLast)
                    result |= 0x4000;
                if (_fSecretEdit)
                    result |= 0x8000;
                return result;
            }
        }

        internal override BiffRecordType RecordType
        {
            get { return BiffRecordType.TxO; }
        }


        public RichTextParagraph GetRichTextParagraph(BiffReaderWorksheetContext context)
        {
            var s = (string) _stringNoCch;
            if (string.IsNullOrEmpty(s))
                return null;
            var result = new RichTextParagraph();
            foreach (var runInfo in _runs.GetIndicePairs())
            {
                if (runInfo.EntIndex> runInfo.StartIndex)
                {
                    var font = context.WorkbookContext.GetFont(runInfo.FontId);
                    var newItem = new RichTextRun(s.Substring(runInfo.StartIndex, runInfo.EntIndex - runInfo.StartIndex));
                    if (font.FontWeight == FontWeights.Bold)
                        newItem.IsBold = true;
                    if (font.FontStyle == FontStyles.Italic)
                        newItem.IsItalic = true;
                    newItem.FontSize = font.Size;
                    newItem.FontFamily = font.FontFamily;
                    newItem.Foreground = font.Foreground;
#if DEBUG
                    var newFont = TxoRuns.GetFont(newItem);
                    Contract.Assert(newFont.Equals(font));
                    Contract.Assert(font.Equals(TxoRuns.GetFont(newItem)));
#endif
                    result.Runs.Add(newItem);
                }
            }
            return result;
        }

        protected override void ReadContent(BiffReaderContext context, int length)
        {
#if DEBUG

            long position = context.Reader.BaseStream.Position;
            Contract.Assert(length == Length);
#endif
            ushort ah = context.ReadUInt16();
            _hAlignment = (byte) ((ah >> 1) & 0x7);
            _vAlignment = (byte) ((ah >> 4) & 0x7);
            _fLockText = (ah & (1 << 9)) != 0;
            _fJustLast = (ah & (1 << 13)) != 0;
            _fSecretEdit = (ah & (1 << 13)) != 0;
            Contract.Assert(Ah == ah);
            _rot = context.ReadUInt16();
            var previousObj = context.PreviousRecord as ObjRecord;
            if (previousObj != null && previousObj.RequiresControlInfo)
            {
                _controlInfo = new ControlInfo();
                _controlInfo.Read(context);
            }
            else
            {
                _reserved = context.Reader.ReadBytes(6);
            }
            _cchText = context.ReadUInt16();
            _cbRuns = context.ReadUInt16();
            _ifntEmpty = context.ReadUInt16();
            _fmla = new ObjFmla();
            _fmla.Read(context.Reader, false);

#if DEBUG
            Contract.Assert(context.Reader.BaseStream.Position == position + length);
#endif
        }

        internal override void WriteContent(BiffWriterWorkbookContext context)
        {
#if DEBUG

            long position = context.Writer.BaseStream.Position;
#endif
            context.WriteUInt16(Ah);
            context.WriteUInt16(_rot);
            if (_controlInfo != null)
            {
                _controlInfo.Write(context);
            }
            else
            {
                context.Write(new byte[6]);
            }
            _cbRuns = _cchText > 0 ? _runs.TotalByteLength : (ushort)0;
            context.Write(_cchText);
            context.Write(_cbRuns);
            context.Write(_ifntEmpty);
            _fmla.Write(context.Writer);
        }

        internal int FullLength
        {
            get
            {
                if (_cchText == 0)
                    return 0;
                int result = _cchText + _cbRuns + 1;
                ContinueRecord continueRecord = (ContinueRecord) Next;
                byte b = continueRecord.Buffer[0];
                var fHighByte = (b & 0x01) != 0;
                if (fHighByte)
                    result += _cchText;
                return result;

            }
        }
        [Obsolete]
        public void ReadWithContinueObsolete()
        {
            if (_cchText == 0)
            {
                Contract.Assert(!(Next is ContinueRecord));
                return;
            }
          //  using (var stream = new MemoryStream(GetfollowingContinueBytes()))
            {
                var reader = new MultiBinaryReader(GetFollowingContinueBytesImp2());
                {
                    _stringNoCch.Read(reader.Reader, _cchText);
                    if (_cbRuns > 0)
                    {
                        _runs = new TxoRuns();
                        _runs.Read(reader, (_cbRuns/8 - 1));
                    }
                   // Contract.Assert(stream.Position == FullLength);
                }
            }
        }
        public override void ReadWithContinue()
        {
            if (_cchText == 0)
            {
                Contract.Assert(!(Next is ContinueRecord));
                return;
            }
            List<byte[]> buffers = Enumerable.ToList(Enumerable.Select(GetFollowingContinueRecords(), r => r.Buffer));
            MultiBinaryReader readers = new MultiBinaryReader(buffers);
            _stringNoCch.ReadMulti(readers, _cchText);
            if (_cbRuns > 0)
            {
                _runs = new TxoRuns();
                _runs.ReadMulti(readers, (_cbRuns / 8 - 1));
            }
            /*
            using (var stream = new MemoryStream(GetfollowingContinueBytes()))
            {
                using (var reader = new BinaryReader(stream))
                {
                    _stringNoCch.Read(reader, _cchText);
                    if (_cbRuns > 0)
                    {
                        _runs = new TxoRuns();
                        _runs.Read(reader, (_cbRuns / 8 - 1));
                    }
                    Contract.Assert(stream.Position == FullLength);
                }
            }*/
        }

        private byte[] GetfollowingContinueBytes()
        {
            return new List<byte>(GetFollowingContinueBytesImp()).ToArray();
        }
        private IEnumerable<byte> GetFollowingContinueBytesImp()
        {
            return Enumerable.SelectMany(GetFollowingContinueRecords(), r => r.Buffer);
        }
        private IEnumerable<byte[]> GetFollowingContinueBytesImp2()
        {
            return Enumerable.Select(GetFollowingContinueRecords(), r => r.Buffer);
        }
       

        public IEnumerable<ContinueRecord> GetContinueRecords()
        {
            if (this._cchText == 0)
                yield break;
            var bytes = new byte[_stringNoCch.TotalByteLength];
            using (var stream = new MemoryStream(bytes))
                using (var writer = new BinaryWriter(stream))
                    _stringNoCch.Write(writer);
            foreach (var continueRecord in ContinueRecordHelper.GetRecords(bytes))
                yield return continueRecord;
            bytes = new byte[_runs.TotalByteLength];
            using (var stream = new MemoryStream(bytes))
            using (var writer = new BinaryWriter(stream))
            {
                _runs.Write(writer);
                Contract.Assert(stream.Position == _runs.TotalByteLength);
            }
            foreach (var continueRecord in ContinueRecordHelper.GetRecords(bytes))
                yield return continueRecord;

        }
    }
}